package b.gate.pcks

import scala.tools.scalap.scalax.rules.scalasig._

/**
  * 从[net.liftweb的lift-json]学来的。
  *
  * 贲国称 于 16/9/7 创建。
  */
object SerializerUtils {
  def findScalaSig(clazz: Class[_]): Option[ScalaSig] =
    ScalaSigParser.parse(clazz).orElse(findScalaSig(clazz.getDeclaringClass))
  def findClass(sig: ScalaSig, clazz: Class[_]): Option[ClassSymbol] = {
    sig.symbols.collect { case c: ClassSymbol if !c.isModule => c }.find(_.name == clazz.getSimpleName).orElse {
      sig.topLevelClasses.find(_.symbolInfo.name == clazz.getSimpleName).orElse {
        sig.topLevelObjects.map { obj =>
          val t = obj.infoType.asInstanceOf[TypeRefType]
          t.symbol.children collect { case c: ClassSymbol => c } find(_.symbolInfo.name == clazz.getSimpleName)
        }.head
      }
    }
  }
  def findClass(clazz: Class[_]): ClassSymbol = {
    val sig = findScalaSig(clazz).getOrElse(throw new Error("Can't find ScalaSig for " + clazz))
    findClass(sig, clazz).getOrElse(throw new Error("Can't find " + clazz + " from parsed ScalaSig"))
  }
  def findConstructor(c: ClassSymbol, argNames: List[String]): Option[MethodSymbol] = {
    val ms = c.children collect { case m: MethodSymbol if m.name == "<init>" => m }
    ms.find(m => m.children.map(_.name) == argNames)
  }
  def toClass(s: Symbol) = s.path match {
    case "scala.Short"   => classOf[Short]
    case "scala.Int"     => classOf[Int]
    case "scala.Long"    => classOf[Long]
    case "scala.Boolean" => classOf[Boolean]
    case "scala.Float"   => classOf[Float]
    case "scala.Double"  => classOf[Double]
    case _               => classOf[AnyRef]
  }
  def isPrimitive(s: Symbol) = toClass(s) != classOf[AnyRef]
  def findArgType(s: MethodSymbol, argIdx: Int, typeArgIndex: Int): Class[_] = {
    def findPrimitive(t: Type): Symbol = t match {
      case TypeRefType(ThisType(_), symbol, _) if isPrimitive(symbol) => symbol
      case TypeRefType(_, _, TypeRefType(ThisType(_), symbol, _) :: xs) => symbol
      case TypeRefType(_, symbol, Nil) => symbol
      case TypeRefType(_, _, vArgs) if typeArgIndex >= vArgs.length => findPrimitive(vArgs(0))
      case TypeRefType(_, _, vArgs) =>
        vArgs(typeArgIndex) match {
          case ref @ TypeRefType(_, _, _) => findPrimitive(ref)
          case x => throw new Error("Unexpected type info " + x)
        }
      case x => throw new Error("Unexpected type info " + x)
    }
    toClass(findPrimitive(s.children(argIdx).asInstanceOf[SymbolInfoSymbol].infoType))
  }
  def findPrimitiveClass(clazz: Class[_], vArgs: List[String], theArg: String): Class[_] = {
    val c = findClass(clazz)
    val cc = findConstructor(c, vArgs).getOrElse(throw new Error("Can't find constructor for " + clazz))
    findArgType(cc, vArgs.indexOf(theArg), 0)
  }
}
